using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml;
using System.Windows.Forms;

namespace ScreenMonkey.Plugin.Clips.Song
{
    public class SongLyrics
    {
        public event EventHandler LabelAdded;
        public event EventHandler LabelRemoved;
        public event EventHandler SequenceChanged;

        public SongLyrics()
        {

        }

        public SongLyrics(string filename)
        {
            XmlDocument songXml = new XmlDocument();

            songXml.Load(filename);
            Load(songXml.SelectSingleNode("Song"));
        }

        public SongLyrics(SongLyrics source)
        {
            Copy(source);
        }

        public void Copy(SongLyrics source)
        {
            this.Title = source.Title;
            this.Author = source.Author;
            this.Year = source.Year;
            this.Owner = source.Owner;
            this.Reference = source.Reference;
            this.FileName = source.FileName;

            Verses.Clear();
            foreach (KeyValuePair<VerseLabel, VerseLyrics> item in source.Verses)
            {
                this.Verses.Add(item.Key, new VerseLyrics(item.Value));
            }

            verseHistory = new List<VerseLabel>(source.verseHistory);
            
            this.UpdateVerseOrder(source.VerseOrder());

            this.labels = source.Labels;
        }

        private string title = string.Empty;

        public string Title
        {
            get { return title; }
            set { title = value; }
        }
	
        private string author = string.Empty;

        public string Author
        {
            get { return author; }
            set { author = value; }
        }

        private string year = string.Empty;
        public string Year
        {
            get
            {
                return year;
            }
            set
            {
                year = value;
            }
        }

        private string owner = string.Empty;

        public string Owner
        {
            get { return owner; }
            set { owner = value; }
        }

        private static string license = string.Empty;

        public static string License
        {
            get { return license; }
            set { license = value; }
        }
	

        public string Copyright
        {
            get
            {
                string licenseAppend = string.Empty;
                if(!string.IsNullOrEmpty(License)) licenseAppend = string.Format(", {0}", License);

                if (Year != "" || Owner != "")
                {
                    return string.Format("{0}, \u00a9 {1} {2}{3}", Author, Year, Owner, licenseAppend);
                }
                else if (!string.IsNullOrEmpty(Author))
                    return Author + licenseAppend;
                else
                    return License;
            }
        }

        private string reference = string.Empty;

        public string Reference
        {
            get { return reference; }
            set { reference = value; }
        }

        private string fileName = "";

        public string FileName
        {
            get { return fileName; }
            set { fileName = value; }
        }

        private List<string> labels = new List<string>();

        public List<string> Labels
        {
            get { return new List<string>(labels); }
        }

        public void AddLabel(string label)
        {
            if (label != null && label != string.Empty)
            {
                if (!labels.Contains(label))
                {
                    labels.Add(label);
                    SongLabels.NewLabel(label);

                    if (LabelAdded != null) LabelAdded(this, EventArgs.Empty);
                }
            }
        }

        public void RemoveLabel(string label)
        {
            if (labels.Remove(label))
            {
                if (LabelRemoved != null) LabelRemoved(this, EventArgs.Empty);
            }
        }

        public bool MatchLabel(string label)
        {
            return Labels.Contains(label);
        }
	
	
        private Dictionary<VerseLabel,VerseLyrics> verses = new Dictionary<VerseLabel,VerseLyrics>();

        public Dictionary<VerseLabel,VerseLyrics> Verses
        {
            get { return verses; }
        }

        private List<VerseLabel> sequence = new List<VerseLabel>();

        public List<VerseLyrics> Sequence
        {
            get
            {
                List<VerseLyrics> order = new List<VerseLyrics>();

                foreach (VerseLabel verse in sequence)
                {
                    if(VerseAvailable(verse))
                    {
                        order.Add(verses[verse]);
                    }
                }

                return order;
            }
        }

        public bool VerseAvailable(VerseLabel verse)
        {
            return verses.ContainsKey(verse);
        }

        private List<VerseLabel> verseHistory = new List<VerseLabel>();

        //Updates the sequence to ensure it is synchronized to the verses.
        private void RefreshSequence()
        {
            bool changed = false;

            //Remove any old verses to the sequence
            foreach(VerseLabel item in new List<VerseLabel>(sequence))
            {
                if(!VerseAvailable(item))
                {
                    sequence.Remove(item);
                    verseHistory.Remove(item);

                    changed = true;
                }
            }

            //Add any new verses to the sequence
            foreach (KeyValuePair<VerseLabel, VerseLyrics> verse in verses)
            {
                if(!sequence.Contains(verse.Key) && !verseHistory.Contains(verse.Key))
                {
                    sequence.Add(verse.Key);
                    verseHistory.Add(verse.Key);

                    changed = true;
                }
            }

            if (changed)
            {
                if (SequenceChanged != null) SequenceChanged(this, EventArgs.Empty);
            }
        }

        public string VerseOrder()
        {
            string orderString = "";

            foreach (VerseLabel item in new List<VerseLabel>(sequence))
            {
                orderString += item.QuickName;
            }

            return orderString;
        }

        public void UpdateVerseOrder(string newOrder)
        {
            VerseLabel quickNameLabel;

            if (newOrder != VerseOrder())
            {
                sequence.Clear();

                foreach (char letter in newOrder.ToCharArray())
                {
                    quickNameLabel = VerseLabels.FindQuickName(letter.ToString());

                    if(quickNameLabel!=null)
                    {
                        if (VerseAvailable(quickNameLabel))
                        {
                            sequence.Add(quickNameLabel);
                        }
                    }

                }

                if (SequenceChanged != null) SequenceChanged(this, EventArgs.Empty);
            }

        }

        public void UpdateWords(string text)
        {
            SongLyrics newWords = SongLyrics.Parse(text);

            verses = newWords.Verses;

            RefreshSequence();
        }

        public static SongLyrics Parse(string text)
        {
            SongLyrics newLyrics = new SongLyrics();
            VerseLyrics newVerse = null;
            VerseLabel verse = VerseLabel.PlainText;

            string line = "";
            StringReader reader = new StringReader(text);

            //while (!VerseLabel.TryParse(line, out verse) && line != null) line = reader.ReadLine();

            while (line != null)
            {
                newVerse = new VerseLyrics();
                newVerse.Label = verse;
                line = "";
                while (!VerseLabel.TryParse(line, out verse) && line != null)
                {
                    newVerse.Words += line.Trim() + "\n";
                    line = reader.ReadLine();
                }

                newVerse.Words = newVerse.Words.Trim(' ', '\n');

                if (newVerse.Words != "")
                {
                    VerseLyrics existingVerse;

                    //Check whether we already have this verse.
                    if (newLyrics.Verses.TryGetValue(newVerse.Label, out existingVerse))
                    {
                        existingVerse.Words += "\n\n" + newVerse.Words;
                    }
                    else
                    {
                        newLyrics.Verses.Add(newVerse.Label, newVerse);
                    }
                }
            }               

            return newLyrics;  
        }

        public override string ToString()
        {
            //string words = "";

            //foreach (VerseLyrics verse in Verses.Values)
            //{
            //    words += verse.ToString() + "\n";
            //}

            return Title;
        }

        public void Save(System.Xml.XmlWriter writer)
        {
            writer.WriteStartElement("Song");
            writer.WriteElementString("Title", Title);
            writer.WriteElementString("Author", Author);
            writer.WriteElementString("Year", Year);
            writer.WriteElementString("Owner", Owner);
            writer.WriteElementString("Reference", Reference);

            //Verse Sequence
            writer.WriteStartElement("Sequence");
            writer.WriteAttributeString("code", VerseOrder());
            foreach(VerseLabel verse in sequence)
            {
                writer.WriteElementString("Verse", verse.Id);
            }
            writer.WriteEndElement();


            writer.WriteElementString("Filename", FileName);

            writer.WriteStartElement("Labels");
            foreach (string label in Labels)
                writer.WriteElementString("Label",label);
            writer.WriteEndElement();

            writer.WriteStartElement("Lyrics");

            foreach(KeyValuePair<VerseLabel,VerseLyrics> item in Verses)
            {
                writer.WriteStartElement("Verse");
                writer.WriteAttributeString("label", item.Value.Label.Id);
                writer.WriteString(item.Value.Words);
                writer.WriteEndElement();
            }

            writer.WriteEndElement();

            writer.WriteEndElement();
        }

        //Check whether the XML file is a song xml file.
        public static bool IsSongXmlFile(FileInfo file)
        {
            XmlDocument testXml = new XmlDocument();

            testXml.Load(file.FullName);
            if(testXml.SelectSingleNode("/Song/Lyrics/Verse") is XmlNode)
            {
                return true;
            }

            return false;
        }

        public void Load(System.Xml.XmlNode xml)
        {
            XmlNode songXml;
            VerseLabel newLabel;

            Verses.Clear();         //Remove all previous verse words.

            originalXml = xml.InnerText;

            if (xml.Name == "Song")
                songXml = xml;
            else
                songXml = xml.SelectSingleNode("Song");

            if (songXml is XmlNode)
            {
                XmlNode titleXml = songXml.SelectSingleNode("Title");
                XmlNode authorXml = songXml.SelectSingleNode("Author");
                XmlNode yearXml = songXml.SelectSingleNode("Year");
                XmlNode ownerXml = songXml.SelectSingleNode("Owner");
                XmlNode referenceXml = songXml.SelectSingleNode("Reference");

                XmlNode sequenceXml = songXml.SelectSingleNode("Sequence/@code");
                if (sequenceXml == null)        //If the code does not exist then this is the old format.
                {
                    sequenceXml = songXml.SelectSingleNode("Sequence/text()");
                }


                XmlNode filenameXml = songXml.SelectSingleNode("Filename/text()");

                if (titleXml is XmlNode) Title = titleXml.InnerText;
                if (authorXml is XmlNode) Author = authorXml.InnerText;
                if (yearXml is XmlNode) Year = yearXml.InnerText;
                if (ownerXml is XmlNode) Owner = ownerXml.InnerText;
                if (referenceXml is XmlNode) Reference = referenceXml.InnerText;
                
                if (filenameXml is XmlNode) FileName = filenameXml.Value;

                //Load Labels
                foreach (XmlNode labelNode in songXml.SelectNodes("Labels/Label/text()"))
                {
                    AddLabel(labelNode.Value);
                }

                foreach (XmlNode verseXml in songXml.SelectNodes("Lyrics/Verse"))
                {
                    string label = verseXml.Attributes.GetNamedItem("label").Value;
                    newLabel = VerseLabels.FindLabel(label);

                    if (newLabel != null)
                    {
                        Verses.Add(newLabel, new VerseLyrics(newLabel, verseXml.InnerText));
                        Body += verseXml.InnerText;
                    }
                }

                RefreshSequence();
                if (sequenceXml is XmlNode) UpdateVerseOrder(sequenceXml.Value);
            }
            else
            {
                throw new LoadLyricsException("Unable to find any song Xml. The xml is in the wrong format or not a song.");
            }
        }

        private string originalXml= "";

        public string OriginalXml
        {
            get { return originalXml; }
        }

        private string body = string.Empty;

        public string Body
        {
            get 
            {                 
                return body; 
            }
            protected set { body = value; }
        }
	
	
    }
}

public class LoadLyricsException:ApplicationException
{
    public LoadLyricsException(string message):base(message)
    {

    }

}
